home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Tcl-Tk 8.0 / Pre-installed version / tk8.0 / win / tkWinButton.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-15  |  22.5 KB  |  808 lines  |  [TEXT/CWIE]

  1. /* 
  2.  * tkWinButton.c --
  3.  *
  4.  *    This file implements the Windows specific portion of the button
  5.  *    widgets.
  6.  *
  7.  * Copyright (c) 1996 by Sun Microsystems, Inc.
  8.  *
  9.  * See the file "license.terms" for information on usage and redistribution
  10.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  11.  *
  12.  * SCCS: @(#) tkWinButton.c 1.11 97/06/18 15:45:07
  13.  */
  14.  
  15. #define OEMRESOURCE
  16. #include "tkWinInt.h"
  17. #include "tkButton.h"
  18.  
  19. /*
  20.  * These macros define the base style flags for the different button types.
  21.  */
  22.  
  23. #define LABEL_STYLE (BS_OWNERDRAW | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS)
  24. #define PUSH_STYLE (BS_OWNERDRAW | BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS)
  25. #define CHECK_STYLE (BS_OWNERDRAW | BS_CHECKBOX | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS)
  26. #define RADIO_STYLE (BS_OWNERDRAW | BS_RADIOBUTTON | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS)
  27.  
  28. static DWORD buttonStyles[] = {
  29.     LABEL_STYLE, PUSH_STYLE, CHECK_STYLE, RADIO_STYLE
  30. };
  31.  
  32. /*
  33.  * Declaration of Windows specific button structure.
  34.  */
  35.  
  36. typedef struct WinButton {
  37.     TkButton info;        /* Generic button info. */
  38.     WNDPROC oldProc;        /* Old window procedure. */
  39.     HWND hwnd;            /* Current window handle. */
  40.     Pixmap pixmap;        /* Bitmap for rendering the button. */
  41.     DWORD style;        /* Window style flags. */
  42. } WinButton;
  43.  
  44.  
  45. /*
  46.  * The following macro reverses the order of RGB bytes to convert
  47.  * between RGBQUAD and COLORREF values.
  48.  */
  49.  
  50. #define FlipColor(rgb) (RGB(GetBValue(rgb),GetGValue(rgb),GetRValue(rgb)))
  51.  
  52. /*
  53.  * The following enumeration defines the meaning of the palette entries
  54.  * in the "buttons" image used to draw checkbox and radiobutton indicators.
  55.  */
  56.  
  57. enum {
  58.     PAL_CHECK = 0,
  59.     PAL_TOP_OUTER = 1,
  60.     PAL_BOTTOM_OUTER = 2,
  61.     PAL_BOTTOM_INNER = 3,
  62.     PAL_INTERIOR = 4,
  63.     PAL_TOP_INNER = 5,
  64.     PAL_BACKGROUND = 6
  65. };
  66.  
  67. /*
  68.  * Set to non-zero if this module is initialized.
  69.  */
  70.  
  71. static int initialized = 0;
  72.  
  73. /*
  74.  * Variables for the cached information about the boxes bitmap.
  75.  */
  76.  
  77. static BITMAPINFOHEADER *boxesPtr = NULL;   /* Information about the bitmap. */
  78. static DWORD *boxesPalette = NULL;        /* Pointer to color palette. */
  79. static LPSTR boxesBits = NULL;            /* Pointer to bitmap data. */
  80. static DWORD boxHeight = 0, boxWidth = 0;    /* Size of each sub-image. */
  81.  
  82. /*
  83.  * This variable holds the default border width for a button in string
  84.  * form for use in a Tk_ConfigSpec.
  85.  */
  86.  
  87. static char defWidth[8];
  88.  
  89. /*
  90.  * Declarations for functions defined in this file.
  91.  */
  92.  
  93. static int        ButtonBindProc _ANSI_ARGS_((ClientData clientData,
  94.                 Tcl_Interp *interp, XEvent *eventPtr,
  95.                 Tk_Window tkwin, KeySym keySym));
  96. static LRESULT CALLBACK    ButtonProc _ANSI_ARGS_((HWND hwnd, UINT message,
  97.                 WPARAM wParam, LPARAM lParam));
  98. static DWORD        ComputeStyle _ANSI_ARGS_((WinButton* butPtr));
  99. static Window        CreateProc _ANSI_ARGS_((Tk_Window tkwin,
  100.                 Window parent, ClientData instanceData));
  101. static void        InitBoxes _ANSI_ARGS_((void));
  102. static void        UpdateButtonDefaults _ANSI_ARGS_((void));
  103.  
  104. /*
  105.  * The class procedure table for the button widgets.
  106.  */
  107.  
  108. TkClassProcs tkpButtonProcs = { 
  109.     CreateProc,            /* createProc. */
  110.     TkButtonWorldChanged,    /* geometryProc. */
  111.     NULL            /* modalProc. */ 
  112. };
  113.  
  114.  
  115. /*
  116.  *----------------------------------------------------------------------
  117.  *
  118.  * InitBoxes --
  119.  *
  120.  *    This function load the Tk 3d button bitmap.  "buttons" is a 16 
  121.  *    color bitmap that is laid out such that the top row contains 
  122.  *    the 4 checkbox images, and the bottom row contains the radio 
  123.  *    button images. Note that the bitmap is stored in bottom-up 
  124.  *    format.  Also, the first seven palette entries are used to 
  125.  *    identify the different parts of the bitmaps so we can do the 
  126.  *    appropriate color mappings based on the current button colors.
  127.  *
  128.  * Results:
  129.  *    None.
  130.  *
  131.  * Side effects:
  132.  *    Loads the "buttons" resource.
  133.  *
  134.  *----------------------------------------------------------------------
  135.  */
  136.  
  137. static void
  138. InitBoxes()
  139. {
  140.     HMODULE module = TkWinGetTkModule();
  141.     HRSRC hrsrc;
  142.     HGLOBAL hblk;
  143.     LPBITMAPINFOHEADER newBitmap;
  144.     DWORD size;
  145.  
  146.     hrsrc = FindResource(module, "buttons", RT_BITMAP);
  147.     if (hrsrc) {
  148.     hblk = LoadResource(module, hrsrc);
  149.     boxesPtr = (LPBITMAPINFOHEADER)LockResource(hblk);
  150.     }
  151.  
  152.     /*
  153.      * Copy the DIBitmap into writable memory.
  154.      */
  155.  
  156.     if (boxesPtr != NULL && !(boxesPtr->biWidth % 4)
  157.         && !(boxesPtr->biHeight % 2)) {
  158.     size = boxesPtr->biSize + (1 << boxesPtr->biBitCount) * sizeof(RGBQUAD)
  159.         + boxesPtr->biSizeImage;
  160.     newBitmap = (LPBITMAPINFOHEADER) ckalloc(size);
  161.     memcpy(newBitmap, boxesPtr, size);
  162.     boxesPtr = newBitmap;
  163.     boxWidth = boxesPtr->biWidth / 4;
  164.     boxHeight = boxesPtr->biHeight / 2;
  165.     boxesPalette = (DWORD*) (((LPSTR)boxesPtr) + boxesPtr->biSize);
  166.     boxesBits = ((LPSTR)boxesPalette)
  167.         + ((1 << boxesPtr->biBitCount) * sizeof(RGBQUAD));
  168.     } else {
  169.     boxesPtr = NULL;
  170.     }
  171. }
  172.  
  173. /*
  174.  *----------------------------------------------------------------------
  175.  *
  176.  * UpdateButtonDefaults --
  177.  *
  178.  *    This function retrieves the current system defaults for
  179.  *    the button widgets.
  180.  *
  181.  * Results:
  182.  *    None.
  183.  *
  184.  * Side effects:
  185.  *    Updates the configuration defaults for buttons.
  186.  *
  187.  *----------------------------------------------------------------------
  188.  */
  189.  
  190. void
  191. UpdateButtonDefaults()
  192. {
  193.     Tk_ConfigSpec *specPtr;
  194.     int width = GetSystemMetrics(SM_CXEDGE);
  195.  
  196.     if (width == 0) {
  197.     width = 1;
  198.     }
  199.     sprintf(defWidth, "%d", width);
  200.     for (specPtr = tkpButtonConfigSpecs; specPtr->type != TK_CONFIG_END;
  201.         specPtr++) {
  202.     if (specPtr->offset == Tk_Offset(TkButton, borderWidth)) {
  203.         specPtr->defValue = defWidth;
  204.     }
  205.     }
  206. }
  207.  
  208. /*
  209.  *----------------------------------------------------------------------
  210.  *
  211.  * TkpCreateButton --
  212.  *
  213.  *    Allocate a new TkButton structure.
  214.  *
  215.  * Results:
  216.  *    Returns a newly allocated TkButton structure.
  217.  *
  218.  * Side effects:
  219.  *    Registers an event handler for the widget.
  220.  *
  221.  *----------------------------------------------------------------------
  222.  */
  223.  
  224. TkButton *
  225. TkpCreateButton(tkwin)
  226.     Tk_Window tkwin;
  227. {
  228.     WinButton *butPtr;
  229.  
  230.     if (!initialized) {
  231.     UpdateButtonDefaults();
  232.     initialized = 1;
  233.     }
  234.  
  235.     butPtr = (WinButton *)ckalloc(sizeof(WinButton));
  236.     butPtr->hwnd = NULL;
  237.     return (TkButton *) butPtr;
  238. }
  239.  
  240. /*
  241.  *----------------------------------------------------------------------
  242.  *
  243.  * CreateProc --
  244.  *
  245.  *    This function creates a new Button control, subclasses
  246.  *    the instance, and generates a new Window object.
  247.  *
  248.  * Results:
  249.  *    Returns the newly allocated Window object, or None on failure.
  250.  *
  251.  * Side effects:
  252.  *    Causes a new Button control to come into existence.
  253.  *
  254.  *----------------------------------------------------------------------
  255.  */
  256.  
  257. static Window
  258. CreateProc(tkwin, parentWin, instanceData)
  259.     Tk_Window tkwin;        /* Token for window. */
  260.     Window parentWin;        /* Parent of new window. */
  261.     ClientData instanceData;    /* Button instance data. */
  262. {
  263.     Window window;
  264.     HWND parent;
  265.     char *class;
  266.     WinButton *butPtr = (WinButton *)instanceData;
  267.  
  268.     parent = Tk_GetHWND(parentWin);
  269.     if (butPtr->info.type == TYPE_LABEL) {
  270.     class = "STATIC";
  271.     butPtr->style = SS_OWNERDRAW | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS;
  272.     } else {
  273.     class = "BUTTON";
  274.     butPtr->style = BS_OWNERDRAW | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS;
  275.     }
  276.     butPtr->hwnd = CreateWindow(class, NULL, butPtr->style,
  277.         Tk_X(tkwin), Tk_Y(tkwin), Tk_Width(tkwin), Tk_Height(tkwin),
  278.         parent, NULL, Tk_GetHINSTANCE(), NULL);
  279.     SetWindowPos(butPtr->hwnd, HWND_TOP, 0, 0, 0, 0,
  280.             SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
  281.     butPtr->oldProc = (WNDPROC)SetWindowLong(butPtr->hwnd, GWL_WNDPROC,
  282.         (DWORD) ButtonProc);
  283.  
  284.     window = Tk_AttachHWND(tkwin, butPtr->hwnd);
  285.     return window;
  286. }
  287.  
  288. /*
  289.  *----------------------------------------------------------------------
  290.  *
  291.  * TkpDestroyButton --
  292.  *
  293.  *    Free data structures associated with the button control.
  294.  *
  295.  * Results:
  296.  *    None.
  297.  *
  298.  * Side effects:
  299.  *    Restores the default control state.
  300.  *
  301.  *----------------------------------------------------------------------
  302.  */
  303.  
  304. void
  305. TkpDestroyButton(butPtr)
  306.     TkButton *butPtr;
  307. {
  308.     WinButton *winButPtr = (WinButton *)butPtr;
  309.     HWND hwnd = winButPtr->hwnd;
  310.     if (hwnd) {
  311.     SetWindowLong(hwnd, GWL_WNDPROC, (DWORD) winButPtr->oldProc);
  312.     }
  313. }
  314.  
  315. /*
  316.  *----------------------------------------------------------------------
  317.  *
  318.  * TkpDisplayButton --
  319.  *
  320.  *    This procedure is invoked to display a button widget.  It is
  321.  *    normally invoked as an idle handler.
  322.  *
  323.  * Results:
  324.  *    None.
  325.  *
  326.  * Side effects:
  327.  *    Information appears on the screen.  The REDRAW_PENDING flag
  328.  *    is cleared.
  329.  *
  330.  *----------------------------------------------------------------------
  331.  */
  332.  
  333. void
  334. TkpDisplayButton(clientData)
  335.     ClientData clientData;    /* Information about widget. */
  336. {
  337.     TkWinDCState state;
  338.     HDC dc;
  339.     register TkButton *butPtr = (TkButton *) clientData;
  340.     GC gc;
  341.     Tk_3DBorder border;
  342.     Pixmap pixmap;
  343.     int x = 0;            /* Initialization only needed to stop
  344.                  * compiler warning. */
  345.     int y, relief;
  346.     register Tk_Window tkwin = butPtr->tkwin;
  347.     int width, height;
  348.     int defaultWidth;        /* Width of default ring. */
  349.     int offset;            /* 0 means this is a label widget.  1 means
  350.                  * it is a flavor of button, so we offset
  351.                  * the text to make the button appear to
  352.                  * move up and down as the relief changes. */
  353.  
  354.     butPtr->flags &= ~REDRAW_PENDING;
  355.     if ((butPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
  356.     return;
  357.     }
  358.  
  359.     border = butPtr->normalBorder;
  360.     if ((butPtr->state == tkDisabledUid) && (butPtr->disabledFg != NULL)) {
  361.     gc = butPtr->disabledGC;
  362.     } else if ((butPtr->state == tkActiveUid)
  363.         && !Tk_StrictMotif(butPtr->tkwin)) {
  364.     gc = butPtr->activeTextGC;
  365.     border = butPtr->activeBorder;
  366.     } else {
  367.     gc = butPtr->normalTextGC;
  368.     }
  369.     if ((butPtr->flags & SELECTED) && (butPtr->state != tkActiveUid)
  370.         && (butPtr->selectBorder != NULL) && !butPtr->indicatorOn) {
  371.     border = butPtr->selectBorder;
  372.     }
  373.  
  374.     /*
  375.      * Override the relief specified for the button if this is a
  376.      * checkbutton or radiobutton and there's no indicator.
  377.      */
  378.  
  379.     relief = butPtr->relief;
  380.     if ((butPtr->type >= TYPE_CHECK_BUTTON) && !butPtr->indicatorOn) {
  381.     relief = (butPtr->flags & SELECTED) ? TK_RELIEF_SUNKEN
  382.         : TK_RELIEF_RAISED;
  383.     }
  384.  
  385.     /*
  386.      * Compute width of default ring and offset for pushed buttons.
  387.      */
  388.  
  389.     if (butPtr->type == TYPE_BUTTON) {
  390.     defaultWidth = ((butPtr->defaultState == tkActiveUid)
  391.         ? butPtr->highlightWidth : 0);
  392.     offset = 1;
  393.     } else {
  394.     defaultWidth = 0;
  395.     if ((butPtr->type >= TYPE_CHECK_BUTTON) && !butPtr->indicatorOn) {
  396.         offset = 1;
  397.     } else {
  398.         offset = 0;
  399.     }
  400.     }
  401.  
  402.     /*
  403.      * In order to avoid screen flashes, this procedure redraws
  404.      * the button in a pixmap, then copies the pixmap to the
  405.      * screen in a single operation.  This means that there's no
  406.      * point in time where the on-sreen image has been cleared.
  407.      */
  408.  
  409.     pixmap = Tk_GetPixmap(butPtr->display, Tk_WindowId(tkwin),
  410.         Tk_Width(tkwin), Tk_Height(tkwin), Tk_Depth(tkwin));
  411.     Tk_Fill3DRectangle(tkwin, pixmap, border, 0, 0, Tk_Width(tkwin),
  412.         Tk_Height(tkwin), 0, TK_RELIEF_FLAT);
  413.  
  414.     /*
  415.      * Display image or bitmap or text for button.
  416.      */
  417.  
  418.     if (butPtr->image != None) {
  419.     Tk_SizeOfImage(butPtr->image, &width, &height);
  420.  
  421.     imageOrBitmap:
  422.     TkComputeAnchor(butPtr->anchor, tkwin, 0, 0,
  423.         butPtr->indicatorSpace + width, height, &x, &y);
  424.     x += butPtr->indicatorSpace;
  425.  
  426.     if (relief == TK_RELIEF_SUNKEN) {
  427.         x += offset;
  428.         y += offset;
  429.     }
  430.     if (butPtr->image != NULL) {
  431.         if ((butPtr->selectImage != NULL) && (butPtr->flags & SELECTED)) {
  432.         Tk_RedrawImage(butPtr->selectImage, 0, 0, width, height,
  433.             pixmap, x, y);
  434.         } else {
  435.         Tk_RedrawImage(butPtr->image, 0, 0, width, height, pixmap,
  436.             x, y);
  437.         }
  438.     } else {
  439.         XSetClipOrigin(butPtr->display, gc, x, y);
  440.         XCopyPlane(butPtr->display, butPtr->bitmap, pixmap, gc, 0, 0,
  441.             (unsigned int) width, (unsigned int) height, x, y, 1);
  442.         XSetClipOrigin(butPtr->display, gc, 0, 0);
  443.     }
  444.     y += height/2;
  445.     } else if (butPtr->bitmap != None) {
  446.     Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height);
  447.     goto imageOrBitmap;
  448.     } else {
  449.     RECT rect;
  450.     TkComputeAnchor(butPtr->anchor, tkwin, butPtr->padX, butPtr->padY,
  451.         butPtr->indicatorSpace + butPtr->textWidth, butPtr->textHeight,
  452.         &x, &y);
  453.  
  454.     x += butPtr->indicatorSpace;
  455.  
  456.     if (relief == TK_RELIEF_SUNKEN) {
  457.         x += offset;
  458.         y += offset;
  459.     }
  460.     Tk_DrawTextLayout(butPtr->display, pixmap, gc, butPtr->textLayout,
  461.         x, y, 0, -1);
  462.     Tk_UnderlineTextLayout(butPtr->display, pixmap, gc,
  463.         butPtr->textLayout, x, y, butPtr->underline);
  464.  
  465.     /*
  466.      * Draw the focus ring.  If this is a push button then we need to put
  467.      * it around the inner edge of the border, otherwise we put it around
  468.      * the text.
  469.      */
  470.  
  471.     if (butPtr->flags & GOT_FOCUS && butPtr->type != TYPE_LABEL) {
  472.         dc = TkWinGetDrawableDC(butPtr->display, pixmap, &state);
  473.         if (butPtr->type == TYPE_BUTTON || !butPtr->indicatorOn) {
  474.         rect.top = butPtr->borderWidth + 1 + defaultWidth;
  475.         rect.left = rect.top;
  476.         rect.right = Tk_Width(tkwin) - rect.left;
  477.         rect.bottom = Tk_Height(tkwin) - rect.top;
  478.         } else {
  479.         rect.top = y-2;
  480.         rect.left = x-2;
  481.         rect.right = x+butPtr->textWidth + 1;
  482.         rect.bottom = y+butPtr->textHeight + 1;
  483.         }
  484.         SetTextColor(dc, gc->foreground);
  485.         SetBkColor(dc, gc->background);
  486.         DrawFocusRect(dc, &rect);
  487.         TkWinReleaseDrawableDC(pixmap, dc, &state);
  488.     }
  489.     y += butPtr->textHeight/2;
  490.     }
  491.  
  492.     /*
  493.      * Draw the indicator for check buttons and radio buttons.  At this
  494.      * point x and y refer to the top-left corner of the text or image
  495.      * or bitmap.
  496.      */
  497.  
  498.     if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn
  499.         && boxesPtr) {
  500.     int xSrc, ySrc;
  501.  
  502.     x -= butPtr->indicatorSpace;
  503.     y -= butPtr->indicatorDiameter / 2;
  504.  
  505.     xSrc = (butPtr->flags & SELECTED) ? boxWidth : 0;
  506.     if (butPtr->state == tkActiveUid) {
  507.         xSrc += boxWidth*2;
  508.     }
  509.     ySrc = (butPtr->type == TYPE_RADIO_BUTTON) ? 0 : boxHeight;
  510.         
  511.     /*
  512.      * Update the palette in the boxes bitmap to reflect the current
  513.      * button colors.  Note that this code relies on the layout of the
  514.      * bitmap's palette.  Also, all of the colors used to draw the
  515.      * bitmap must be in the palette that is selected into the DC of
  516.      * the offscreen pixmap.  This requires that the static colors
  517.      * be placed into the palette.
  518.      */
  519.  
  520.     boxesPalette[PAL_CHECK] = FlipColor(gc->foreground);
  521.     boxesPalette[PAL_TOP_OUTER] = FlipColor(TkWinGetBorderPixels(tkwin,
  522.         border, TK_3D_DARK_GC));
  523.     boxesPalette[PAL_TOP_INNER] = FlipColor(TkWinGetBorderPixels(tkwin,
  524.         border, TK_3D_DARK2));
  525.     boxesPalette[PAL_BOTTOM_INNER] = FlipColor(TkWinGetBorderPixels(tkwin,
  526.         border, TK_3D_LIGHT2));
  527.     boxesPalette[PAL_BOTTOM_OUTER] = FlipColor(TkWinGetBorderPixels(tkwin,
  528.         border, TK_3D_LIGHT_GC));
  529.     if (butPtr->state == tkDisabledUid) {
  530.         boxesPalette[PAL_INTERIOR] = FlipColor(TkWinGetBorderPixels(tkwin,
  531.         border, TK_3D_LIGHT2));
  532.     } else if (butPtr->selectBorder != NULL) {
  533.         boxesPalette[PAL_INTERIOR] = FlipColor(TkWinGetBorderPixels(tkwin,
  534.             butPtr->selectBorder, TK_3D_FLAT_GC));
  535.     } else {
  536.         boxesPalette[PAL_INTERIOR] = FlipColor(GetSysColor(COLOR_WINDOW));
  537.     }
  538.     boxesPalette[PAL_BACKGROUND] = FlipColor(TkWinGetBorderPixels(tkwin,
  539.         border, TK_3D_FLAT_GC));
  540.  
  541.     dc = TkWinGetDrawableDC(butPtr->display, pixmap, &state);
  542.     StretchDIBits(dc, x, y, boxWidth, boxHeight, xSrc, ySrc, 
  543.         boxWidth, boxHeight, boxesBits, (LPBITMAPINFO)boxesPtr, 
  544.         DIB_RGB_COLORS, SRCCOPY);
  545.     TkWinReleaseDrawableDC(pixmap, dc, &state);
  546.     }
  547.  
  548.     /*
  549.      * If the button is disabled with a stipple rather than a special
  550.      * foreground color, generate the stippled effect.  If the widget
  551.      * is selected and we use a different background color when selected,
  552.      * must temporarily modify the GC.
  553.      */
  554.  
  555.     if ((butPtr->state == tkDisabledUid)
  556.         && ((butPtr->disabledFg == NULL) || (butPtr->image != NULL))) {
  557.     if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn
  558.         && (butPtr->selectBorder != NULL)) {
  559.         XSetForeground(butPtr->display, butPtr->disabledGC,
  560.             Tk_3DBorderColor(butPtr->selectBorder)->pixel);
  561.     }
  562.     XFillRectangle(butPtr->display, pixmap, butPtr->disabledGC,
  563.         butPtr->inset, butPtr->inset,
  564.         (unsigned) (Tk_Width(tkwin) - 2*butPtr->inset),
  565.         (unsigned) (Tk_Height(tkwin) - 2*butPtr->inset));
  566.     if ((butPtr->flags & SELECTED) && !butPtr->indicatorOn
  567.         && (butPtr->selectBorder != NULL)) {
  568.         XSetForeground(butPtr->display, butPtr->disabledGC,
  569.             Tk_3DBorderColor(butPtr->normalBorder)->pixel);
  570.     }
  571.     }
  572.  
  573.     /*
  574.      * Draw the border and traversal highlight last.  This way, if the
  575.      * button's contents overflow they'll be covered up by the border.
  576.      */
  577.  
  578.     if (relief != TK_RELIEF_FLAT) {
  579.     Tk_Draw3DRectangle(tkwin, pixmap, border,
  580.         defaultWidth, defaultWidth,
  581.         Tk_Width(tkwin) - 2*defaultWidth,
  582.         Tk_Height(tkwin) - 2*defaultWidth,
  583.         butPtr->borderWidth, relief);
  584.     }
  585.     if (defaultWidth != 0) {
  586.     dc = TkWinGetDrawableDC(butPtr->display, pixmap, &state);
  587.     TkWinFillRect(dc, 0, 0, Tk_Width(tkwin), defaultWidth,
  588.         butPtr->highlightColorPtr->pixel);
  589.     TkWinFillRect(dc, 0, 0, defaultWidth, Tk_Height(tkwin),
  590.         butPtr->highlightColorPtr->pixel);
  591.     TkWinFillRect(dc, 0, Tk_Height(tkwin) - defaultWidth,
  592.         Tk_Width(tkwin), defaultWidth,
  593.         butPtr->highlightColorPtr->pixel);
  594.     TkWinFillRect(dc, Tk_Width(tkwin) - defaultWidth, 0,
  595.         defaultWidth, Tk_Height(tkwin),
  596.         butPtr->highlightColorPtr->pixel);
  597.     TkWinReleaseDrawableDC(pixmap, dc, &state);
  598.     }
  599.  
  600.     /*
  601.      * Copy the information from the off-screen pixmap onto the screen,
  602.      * then delete the pixmap.
  603.      */
  604.  
  605.     XCopyArea(butPtr->display, pixmap, Tk_WindowId(tkwin),
  606.         butPtr->copyGC, 0, 0, (unsigned) Tk_Width(tkwin),
  607.         (unsigned) Tk_Height(tkwin), 0, 0);
  608.     Tk_FreePixmap(butPtr->display, pixmap);
  609. }
  610.  
  611. /*
  612.  *----------------------------------------------------------------------
  613.  *
  614.  * TkpComputeButtonGeometry --
  615.  *
  616.  *    After changes in a button's text or bitmap, this procedure
  617.  *    recomputes the button's geometry and passes this information
  618.  *    along to the geometry manager for the window.
  619.  *
  620.  * Results:
  621.  *    None.
  622.  *
  623.  * Side effects:
  624.  *    The button's window may change size.
  625.  *
  626.  *----------------------------------------------------------------------
  627.  */
  628.  
  629. void
  630. TkpComputeButtonGeometry(butPtr)
  631.     register TkButton *butPtr;    /* Button whose geometry may have changed. */
  632. {
  633.     int width, height, avgWidth;
  634.     Tk_FontMetrics fm;
  635.  
  636.     if (butPtr->highlightWidth < 0) {
  637.     butPtr->highlightWidth = 0;
  638.     }
  639.     butPtr->inset = butPtr->highlightWidth + butPtr->borderWidth;
  640.     butPtr->indicatorSpace = 0;
  641.  
  642.     if (!boxesPtr) {
  643.     InitBoxes();
  644.     }
  645.  
  646.     if (butPtr->image != NULL) {
  647.     Tk_SizeOfImage(butPtr->image, &width, &height);
  648.     imageOrBitmap:
  649.     if (butPtr->width > 0) {
  650.         width = butPtr->width;
  651.     }
  652.     if (butPtr->height > 0) {
  653.         height = butPtr->height;
  654.     }
  655.     if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
  656.         butPtr->indicatorSpace = boxWidth * 2;
  657.         butPtr->indicatorDiameter = boxHeight;
  658.     }
  659.     } else if (butPtr->bitmap != None) {
  660.     Tk_SizeOfBitmap(butPtr->display, butPtr->bitmap, &width, &height);
  661.     goto imageOrBitmap;
  662.     } else {
  663.     Tk_FreeTextLayout(butPtr->textLayout);
  664.     butPtr->textLayout = Tk_ComputeTextLayout(butPtr->tkfont,
  665.         butPtr->text, -1, butPtr->wrapLength, butPtr->justify, 0,
  666.         &butPtr->textWidth, &butPtr->textHeight);
  667.  
  668.     width = butPtr->textWidth;
  669.     height = butPtr->textHeight;
  670.     avgWidth = Tk_TextWidth(butPtr->tkfont, "0", 1);
  671.     Tk_GetFontMetrics(butPtr->tkfont, &fm);
  672.  
  673.     if (butPtr->width > 0) {
  674.         width = butPtr->width * avgWidth;
  675.     }
  676.     if (butPtr->height > 0) {
  677.         height = butPtr->height * fm.linespace;
  678.     }
  679.  
  680.     if ((butPtr->type >= TYPE_CHECK_BUTTON) && butPtr->indicatorOn) {
  681.         butPtr->indicatorDiameter = boxHeight;
  682.         butPtr->indicatorSpace = butPtr->indicatorDiameter + avgWidth;
  683.     }
  684.  
  685.     /*
  686.      * Increase the inset to allow for the focus ring.
  687.      */
  688.  
  689.     if (butPtr->type != TYPE_LABEL) {
  690.         butPtr->inset += 3;
  691.     }
  692.     }
  693.  
  694.     /*
  695.      * When issuing the geometry request, add extra space for the indicator,
  696.      * if any, and for the border and padding, plus an extra pixel so the
  697.      * display can be offset by 1 pixel in either direction for the raised
  698.      * or lowered effect.
  699.      */
  700.  
  701.     if ((butPtr->image == NULL) && (butPtr->bitmap == None)) {
  702.     width += 2*butPtr->padX;
  703.     height += 2*butPtr->padY;
  704.     }
  705.     if ((butPtr->type == TYPE_BUTTON)
  706.         || ((butPtr->type >= TYPE_CHECK_BUTTON) && !butPtr->indicatorOn)) {
  707.     width += 1;
  708.     height += 1;
  709.     }
  710.     Tk_GeometryRequest(butPtr->tkwin, (int) (width + butPtr->indicatorSpace
  711.         + 2*butPtr->inset), (int) (height + 2*butPtr->inset));
  712.     Tk_SetInternalBorder(butPtr->tkwin, butPtr->inset);
  713. }
  714.  
  715. /*
  716.  *----------------------------------------------------------------------
  717.  *
  718.  * ButtonProc --
  719.  *
  720.  *    This function is call by Windows whenever an event occurs on
  721.  *    a button control created by Tk.
  722.  *
  723.  * Results:
  724.  *    Standard Windows return value.
  725.  *
  726.  * Side effects:
  727.  *    May generate events.
  728.  *
  729.  *----------------------------------------------------------------------
  730.  */
  731.  
  732. static LRESULT CALLBACK
  733. ButtonProc(hwnd, message, wParam, lParam)
  734.     HWND hwnd;
  735.     UINT message;
  736.     WPARAM wParam;
  737.     LPARAM lParam;
  738. {
  739.     LRESULT result;
  740.     WinButton *butPtr;
  741.     Tk_Window tkwin = Tk_HWNDToWindow(hwnd);
  742.  
  743.     if (tkwin == NULL) {
  744.     panic("ButtonProc called on an invalid HWND");
  745.     }
  746.     butPtr = (WinButton *)((TkWindow*)tkwin)->instanceData;
  747.  
  748.     switch(message) {
  749.     case WM_ERASEBKGND:
  750.         return 0;
  751.  
  752.     case BM_GETCHECK:
  753.         if (((butPtr->info.type == TYPE_CHECK_BUTTON)
  754.             || (butPtr->info.type == TYPE_RADIO_BUTTON))
  755.             && butPtr->info.indicatorOn) {
  756.         return (butPtr->info.flags & SELECTED)
  757.             ? BST_CHECKED : BST_UNCHECKED;
  758.         }
  759.         return 0;
  760.  
  761.     case BM_GETSTATE: {
  762.         DWORD state = 0;
  763.         if (((butPtr->info.type == TYPE_CHECK_BUTTON)
  764.             || (butPtr->info.type == TYPE_RADIO_BUTTON))
  765.             && butPtr->info.indicatorOn) {
  766.         state = (butPtr->info.flags & SELECTED)
  767.             ? BST_CHECKED : BST_UNCHECKED;
  768.         }
  769.         if (butPtr->info.flags & GOT_FOCUS) {
  770.         state |= BST_FOCUS;
  771.         }
  772.         return state;
  773.     }
  774.     case WM_ENABLE:
  775.         break;
  776.  
  777.     case WM_PAINT: {
  778.         PAINTSTRUCT ps;
  779.         BeginPaint(hwnd, &ps);
  780.         EndPaint(hwnd, &ps);
  781.         TkpDisplayButton((ClientData)butPtr);
  782.         return 0;
  783.     }
  784.     case BN_CLICKED: {
  785.         int code;
  786.         Tcl_Interp *interp = butPtr->info.interp;
  787.         if (butPtr->info.state != tkDisabledUid) {
  788.         Tcl_Preserve((ClientData)interp);
  789.         code = TkInvokeButton((TkButton*)butPtr);
  790.         if (code != TCL_OK && code != TCL_CONTINUE
  791.             && code != TCL_BREAK) {
  792.             Tcl_AddErrorInfo(interp, "\n    (button invoke)");
  793.             Tcl_BackgroundError(interp);
  794.         }
  795.         Tcl_Release((ClientData)interp);
  796.         }
  797.         Tcl_ServiceAll();
  798.         return 0;
  799.     }
  800.  
  801.     default:
  802.         if (Tk_TranslateWinEvent(hwnd, message, wParam, lParam, &result)) {
  803.         return result;
  804.         }
  805.     }
  806.     return DefWindowProc(hwnd, message, wParam, lParam);
  807. }
  808.